package com.m3958.firstgwt.server.servlet;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.sf.json.JSONObject;

import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Singleton;
import com.google.inject.persist.Transactional;
import com.m3958.firstgwt.server.constants.PeriodContants;
import com.m3958.firstgwt.server.dao.VoteHitDao;
import com.m3958.firstgwt.server.model.Vote;
import com.m3958.firstgwt.server.model.VoteAccess;
import com.m3958.firstgwt.server.model.VoteHit;
import com.m3958.firstgwt.server.model.VoteTpl;
import com.m3958.firstgwt.server.service.RequestScopeObjectService;
import com.m3958.firstgwt.server.service.VoteTplContextFMCfg;
import com.m3958.firstgwt.server.session.SessionUser;
import com.m3958.firstgwt.shared.type.CookieNames;
import com.m3958.firstgwt.shared.type.SmartConstants;

import freemarker.template.Template;
import freemarker.template.TemplateException;


@Singleton
public class VoteServlet extends HttpServlet{
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	@Inject
	private Injector injector;
	
	
	public static class VoteParaName{
		public static String VOTE_ID = "voteid";
		public static String DOVOTE_IDS= "dovoteids";
		public static String OUTPUT = "output";
		public static String ENCODE = "encode";
		public static String TPL_ID = "tplid";
		public static String DOVOTE = "dovote";
	}
	
	public class VoteParaWrapper{
		Map<String, String> paras = new HashMap<String, String>();
		
		public VoteParaWrapper(HttpServletRequest req){
			initMe(req);
		}

		private void initMe(HttpServletRequest req) {
		     for (Enumeration<String> e = req.getParameterNames() ; e.hasMoreElements() ;) {
		    	 String pn = e.nextElement();
		         paras.put(pn, req.getParameter(pn));
		     }
		     if(paras.get(VoteParaName.ENCODE) == null){
		    	 paras.put(VoteParaName.ENCODE, "utf-8");
		     }
		}
		
		public Map<String, String> getAllParas(){
			return paras;
		}
		
		public String getPvalue(String pn){
			String v = paras.get(pn);
			if(v == null)return "";
			return v;
		}
		
		public void setPvalue(String pn,String value){
			paras.put(pn, value);
		}
		
		public int getIdValue(String pn){
			int i = -1;
			try {
				i = Integer.parseInt(getPvalue(pn));
			} catch (NumberFormatException e) {}
			return i;
		}
		
		public int getVoteId(){
			return getIdValue(VoteParaName.VOTE_ID);
		}
		
		public int getTplId(){
			return getIdValue(VoteParaName.TPL_ID);
		}
		
		public boolean isDovote(){
			return !getPvalue(VoteParaName.DOVOTE).isEmpty();
		}
		
		public String getEncode(){
			String ec = getPvalue(VoteParaName.ENCODE); 
			return ec.isEmpty() ? "utf-8" : ec;
		}
		
		public String getOutput(){
			String ec = getPvalue(VoteParaName.OUTPUT); 
			return ec.isEmpty() ? "wholepage" : ec;
		}
		
		public List<List<Vote>> getDoVoteArys(){
			//parameter format:28773-29283~238239-238823
			List<List<Vote>> doVoteArys = new ArrayList<List<Vote>>();
			String[] doVoteidsary = getPvalue(VoteParaName.DOVOTE_IDS).trim().split("~");
			for(String s : doVoteidsary){
				if(!s.trim().isEmpty()){//238239-238823
					String[] vids = s.split("-");
					List<Vote> votes = new ArrayList<Vote>();
					for(String vs : vids){
						if(!vs.trim().isEmpty()){
							votes.add(emp.get().find(Vote.class, Integer.parseInt(vs)));
						}
					}
					doVoteArys.add(votes);
				}
			}
			return doVoteArys;
		}

		
		public boolean outputIsDocumentWrite(){
			return "documentwrite".equals(getPvalue(VoteParaName.OUTPUT));
		}
		
		public boolean outputIsJsonp(){
			return "jsonp".equals(getPvalue(VoteParaName.OUTPUT));
		}
	}
	
	
	@Inject
	protected com.google.inject.Provider<EntityManager> emp;

	@Inject
	private VoteTplContextFMCfg tplLoader;
	
	@Override
	public void doPost(HttpServletRequest req, HttpServletResponse res)
			throws ServletException, IOException {
		doGet(req, res);
	}
	/*
	 * vid
	 * output:html,documentwrite,jsonp
	 * encode:gbk,utf8
	 * tplid
	 * 模板model，vote，votes
	 * Map<String,Object> rootDataModel = new HashMap<String, Object>();
	 */
	
	@Override
	public void doGet(HttpServletRequest req, HttpServletResponse res)
			throws ServletException, IOException {
		
		VoteParaWrapper vpw = new VoteParaWrapper(req);
		
		if(vpw.getTplId() == SmartConstants.NONE_EXIST_MODEL_ID){
			sendError(res, vpw.getEncode(),vpw.getOutput(), "必须指定模板ID，即tplid参数！");
			return;
		}
		
		if(vpw.isDovote()){
			if(!req.getMethod().equalsIgnoreCase("POST")){
				sendError(res, vpw.getEncode(),vpw.getOutput(), "显示投票必须使用POST方法！");
				return;
			}
		}else{
			if(!req.getMethod().equalsIgnoreCase("GET")){
				sendError(res, vpw.getEncode(),vpw.getOutput(), "显示投票必须使用GET方法！");
				return;
			}
		}
		
		if(vpw.getVoteId() == SmartConstants.NONE_EXIST_MODEL_ID){
			sendError(res,vpw.getEncode(),vpw.getOutput(), "voteid参数必须提供！");
			return;
		}

		
		Vote vote = emp.get().find(Vote.class, vpw.getVoteId());
		if(vote == null){
			sendError(res, vpw.getEncode(),vpw.getOutput(), "没有找到voteid对应的vote，请检查！");
			return;
		}
		saveVoteAccess(req, vote);
		//没有付费，并且模板不是系统模板的限定访问次数100次。
		//如果用户使用了你的模板，那么你将从付费中获取20%的收益。
		if(vote.isPaid() || vpw.outputIsDocumentWrite()){
			;
		}else{
			SessionUser su = injector.getInstance(SessionUser.class);
			if(!su.isLogined()){
				VoteTpl vt = emp.get().find(VoteTpl.class, vpw.getTplId());
				if(vt == null){
					sendError(res, vpw.getEncode(),vpw.getOutput(), "没有找到tplid对应的模板，请检查！");
					return;
				}
				if(!"system".equals(vt.getFlag())){
					sendError(res, vpw.getEncode(),vpw.getOutput(), "此模板未经审核！");
					return;
				}
			}
		}
		
		if(vpw.isDovote()){
			doVote(req, res, vpw.getDoVoteArys(),vpw.getAllParas());
		}
		//charset=gbk,utf-8
		res.setContentType("text/html;charset=" + vpw.getEncode());
		try {
			Template template = tplLoader.getTemplate(vpw.getPvalue(VoteParaName.TPL_ID));
			StringWriter sWriter = new StringWriter();
			template.process(prepareRootModel(vpw.getAllParas(),vote), sWriter);
			String content = sWriter.toString();
			if(vpw.outputIsDocumentWrite()){
				StringBuffer sb = new StringBuffer("var m3958_vote_content = ");
				JSONObject jo = new JSONObject().element("vc", content);
				sb.append(jo.toString()).append(";").append("document.write(m3958_vote_content.vc);");
				content = sb.toString();
			}else if(vpw.outputIsJsonp()){
				String callback = req.getParameter("callback");
				JSONObject jo = new JSONObject().element("vc", content);
				StringBuffer sb = new StringBuffer(callback).append("(").append(jo.toString()).append(");");
				content = sb.toString();
			}
			res.setContentLength(content.getBytes(getCaName(vpw.getEncode())).length);
			PrintWriter writer = res.getWriter();
			writer.write(content);
			writer.close();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (TemplateException e) {
			e.printStackTrace();
		}
	}
	
	@Transactional
	public void saveVoteAccess(HttpServletRequest req,Vote vote){
		VoteAccess va = new VoteAccess();
		va.setCreatedAt(new Date());
		va.setReferer(req.getHeader("referer"));
		va.setVote(vote);
		emp.get().persist(va);
		vote.getVoteAccesses().add(va);//维护one一侧的结果的准确性是你的责任。
	}

	@Transactional
	public void doVote(HttpServletRequest req, HttpServletResponse res,List<List<Vote>> doVoteArys,Map<String, String> paras) {
		//如果在同一树枝下，会造成总数统计不准确，必须引入cross参数。
		boolean hasdovote = false;
		Cookie[] cookies = req.getCookies();
		if(cookies != null){
			for(Cookie cookie:cookies){
				if(cookie.getName().equals(CookieNames.HAS_VOTE + paras.get("voteid"))){
					hasdovote = true;
					break;
				}
			}
		}
		if(!hasdovote){
			RequestScopeObjectService rso = injector.getInstance(RequestScopeObjectService.class);
			VoteHitDao vhdao = injector.getInstance(VoteHitDao.class);
			String referer = req.getHeader("referer");
			String ip = rso.getRemoteIp();
			
			for(List<Vote> lv : doVoteArys){
				VoteHit vh = new VoteHit();
				vh.setReferer(referer);
				vh.setIp(ip);
				vhdao.smartPersistBaseModel(vh);
				for(Vote v : lv){
					v.addVoteHits(vh);
					emp.get().merge(v);
				}
			}
			Cookie c = new Cookie(CookieNames.HAS_VOTE + paras.get("voteid"),"true");
			c.setPath("/vote");
			c.setMaxAge(PeriodContants.WEEK_IN_SECONDS);
			res.addCookie(c);
		}
	}

	private String getCaName(String encode) {
		if(encode.toUpperCase().contains("UTF")){
			return "UTF8";
		}else{
			return encode;
		}
	}

	private Map<String,Object> prepareRootModel(Map<String, String> paras,Vote vote) {
		Map<String,Object> rootDataModel = new HashMap<String, Object>();
		rootDataModel.put("vote", vote);
		rootDataModel.put("votes", vote.getChildren());
		rootDataModel.put("util", new Util());
		rootDataModel.put("paras", paras);
		return rootDataModel;
	}
	
	public static class Util{
		public Date getCurrentDate(){
			return new Date();
		}
		
		public boolean beforeNow(Date date){
			return date.getTime() < getCurrentDate().getTime();
		}
		
		public String toJson(Object o){
			return JSONObject.fromObject(o).toString();
		}
	}
	
	private void sendError(HttpServletResponse res,String encode,String output,String error) throws IOException{
		res.setContentType("text/html;charset=" + encode);
		PrintWriter pw = res.getWriter();
		if("documentwrite".equals(output)){
			pw.write("document.write('"+ error +"')");
		}else{
			pw.write(error);
		}
	}

}
